#include <stdlib.h>
#include <stdio.h>

#include "shared.h"
#include "token_arb.h"
#include "flit_queue.h"
#include "power.h"

#define queue_len(q) q->num_flits 

/* 3-step arbitration 
 * 1. (sender) Request generation at most 8
 * 2. (token slot) Access grant
 * 3. (sender) Choose access at most 2
 */

inline void pushback_dly_tap(unsigned int chn, flit_t **flit_ptr); 

void token_arb_stage(void)
{
  int node, chn, pri;
  flit_t *flit_ptr; 
  /* Clear request vector and token vector */
  for(chn = 0;chn < NUM_CHN;chn ++)
  { 
    token[chn] = 0; 
    for(node = 0;node < NUM_NODES;node ++) 
      requests[chn][node] = 0;
  }

  /* 1. Generate request vector */
  for(node = 0;node < NUM_NODES;node ++)
  { 
    unsigned int total_requested = 0; 
    unsigned int recent_requested = last_requested[node]; 
    for(chn = 0;chn < NUM_CHN;chn ++) 
    {
      //bool idle_flag = true;
      unsigned int tmp_chn = (last_requested[node] + chn + 1) % NUM_CHN;
      pri = node_pri_map[node][tmp_chn]; 
      if(inj_queues[node][tmp_chn].num_flits > 0)
      {
        /* Sanity check: a node cannot send to itself */ 
        if(pri == 0) 
        {
          printf("Request error: node %d requests chn %d.\n", node, tmp_chn); 
          exit(1); 
        } 
        flit_ptr = read(&inj_queues[node][tmp_chn]); 
        if(flit_ptr->type == READY)
        {
          last_busy_cycle[node][chn] = SRC_CLK; 
          //idle_flag = false;
          if(total_requested < MAX_REQS) //Request at most MAX_REQS channels
          { 
            requests[tmp_chn][pri] = 1;
            total_requested ++;

            //Energy: 1. Tune into/out of resonance
            dyn_o_power += PD_MOD * 2; 

            if(total_requested == 1) 
              recent_requested = tmp_chn; //Update request history 
            if(VERBOSE)
            { 
              printf("[%d|%d|R] flit num:%d snode:%d dnode:%d\n", 
                      SRC_CLK, node, flit_ptr->flit_num, flit_ptr->snode, 
                      flit_ptr->dnode);
            }
          } 
        }
      }
      ///* If no request is generated, this queue is idle */
      //if(QOS && voq_stat[node][tmp_chn] == BUSY && idle_flag) 
      //{ 
      //  voq_stat[node][tmp_chn] = IDLE; 
      //  comp_counter[tmp_chn] ++;
      //  if(VERBOSE)
      //    printf("[%d|%d|B2I] node %d becomes idle\n", 
      //            SRC_CLK, node, node);  
      //}
      //if(total_requested == MAX_REQS) //Request at most 8 channels
      //  break; 
    }
    last_requested[node] = recent_requested;
  } 
   
  /* Generate token vector, the source queue needs to check 
   * its buffer occupation
   */
  for(chn = 0;chn < NUM_CHN;chn ++)
    if(ejt_queues[chn].num_flits < INJ_BUF_DEPTH - CHN_FLY_TIME) 
    {
      token[chn] = 1; 
      //Energy: 1. E/O emit one bit token
      dyn_o_power += PD_MOD; 
      dyn_e_power += PD_DR; 
    } 

  /* 2. Arbitrate. 
   * Now only support: fixed priority arbitration
   */
  for(chn = 0;chn < NUM_CHN;chn ++)
    if(token[chn] == 1)
    {
      bool winner_found = false; 
      for(pri = 0;pri < NUM_NODES;pri ++)
        if(requests[chn][pri] == 1 && winner_found == false) 
        {
          //Energy: 1. O/E detect one bit token
          dyn_e_power += PD_OE; 
 
          node = chn_map[chn][pri];
          winner_found = true; 
          flit_ptr = read(&inj_queues[node][chn]);  
          if(VERBOSE) 
            printf("[%d|%d|TA] flit num:%d snode:%d dnode:%d\n", 
                    SRC_CLK, flit_ptr->dnode, flit_ptr->flit_num, 
                    flit_ptr->snode, flit_ptr->dnode);  

          //pushback_dly_tap(chn, &flit_ptr);
          //break; 
        }
        else
          requests[chn][pri] = 0;
    }
  /* 3. Finally, the sender will choose from winned channels
   * for sending (at most 2/cycle) 
   */
  for(node = 0;node < NUM_NODES;node ++) 
  {
    unsigned int total_used = 0;
    unsigned int recent_used = last_used[node]; 
    for(chn = 0;chn < NUM_CHN;chn ++) 
    {
      unsigned int tmp_chn = (last_used[node] + chn + 1) % NUM_CHN; 
      pri = node_pri_map[node][tmp_chn];
      if(requests[tmp_chn][pri] == 1) 
      { 
        flit_ptr = recv(&inj_queues[node][tmp_chn]);
        //Energy: 1. E/O emit 256-bit data
        dyn_o_power += PD_MOD * 512; 
        dyn_e_power += PD_DR  * 512; 

        total_used ++; 
        if(total_used == 1) 
          recent_used = tmp_chn; 
        if(VERBOSE)  
          printf("[%d|%d|S] flit num:%d snode:%d dnode:%d\n", 
                  SRC_CLK, flit_ptr->dnode, flit_ptr->flit_num, 
                    flit_ptr->snode, flit_ptr->dnode); 
        pushback_dly_tap(tmp_chn, &flit_ptr);
      }
      if(total_used == MAX_ACCS) 
        break;  //Use at most two channels
    }
    last_used[node] = recent_used;
  } 
}
 
inline void pushback_dly_tap(unsigned int chn, flit_t **flit_ptr)
{
  unsigned int sched_tm = 
    (DLY_TAP_PT + CHN_FLY_TIME - 1)%CHN_FLY_TIME; 

  flt_dly_tap[chn][sched_tm] = *flit_ptr; 
  *flit_ptr = NULL; 
}
